﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Labthink.PCSoft.UniversalHost4.Model;
using Labthink.PCSoft.UniversalHost4.Model.Query;
using Labthink.PCSoft.UniversalHost4.Model.Setting;
using Labthink.PCSoft.UniversalHost4.Utility;
using NLog.Fluent;

namespace Labthink.PCSoft.UniversalHost4.DataAccess
{
    public class DataShieldClient
    {
        private static int _port;//端口号
        private static string _ip;//IP地址
        private static bool _isDataShield;
        private static Queue<string> _files;
        private static Thread _threadSend;
        // ReSharper disable once NotAccessedField.Local
        private static Timer _connectTimer;
        private static bool _connect;
        private static bool _isSending;
        private static EndPoint _remoteEp;
        private static FileSend _sender;
        private static Socket _client;
        private static readonly string DataPath = Path.Combine(DataManage.BasePath, "DataShield");
        private static readonly string SendRecord = Path.Combine(DataPath, "sendrecord");
        private static readonly string SourceRecord = Path.Combine(DataManage.BasePath, "Config") + "//sendrecord";
        private static bool _isAutoDelete;

        public static bool IsDataShield => _isDataShield;

        public static void Start()
        {
            _files = new Queue<string>();
            ReflashCfg();
            if (!_isDataShield) return;

            _connect = true;
            _sender = new FileSend();
            _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _threadSend = new Thread(_send) { IsBackground = true };
            _threadSend.Start();
            //_connectTimer = new Timer(TestConnect, null, 0, 60000);
        }

        public static void Stop()
        {
            _files.Clear();
            _threadSend.Abort();
            _connectTimer.Dispose();
        }

        /// <summary>
        /// 刷新配置信息
        /// </summary>
        public static void ReflashCfg()
        {
            var ini = Path.Combine(DataManage.BasePath, "Config\\AppConfig.ini");
            _isDataShield = IniHelper.GetIni(ini, "base", "datashield", "0").ToInt32() > 0;

            if (_isDataShield)
            {
                if (!Directory.Exists(DataPath))
                {
                    Directory.CreateDirectory(DataPath);
                }

                if (!File.Exists(SendRecord))
                {
                    if (!File.Exists(SourceRecord))
                    {
                        throw new FileNotFoundException("未找到文件 " + SourceRecord);
                    }
                    else
                    {
                        File.Copy(SourceRecord, SendRecord, true);
                    }
                }
                else
                {
                    //SQLiteConnection.CompressFile(SendRecord);
                }

                _ip = IniHelper.GetIni(ini, "datashield", "ip", "192.168.22.100");
                _port = IniHelper.GetIni(ini, "datashield", "port", "10000").ToInt32();
                _isAutoDelete = IniHelper.GetIni(ini, "datashield", "autodelete", "1").ToInt32() > 0;
                _remoteEp = new IPEndPoint(IPAddress.Parse(_ip), _port);
                _connect = true;
                GetFiles();
            }
        }

        /// <summary>
        /// 获取未发送的文件列表
        /// </summary>
        /// <returns></returns>
        public static PagedList<string> GetNoSendFile(QueryBase query)
        {
            var folder = new DirectoryInfo(DataPath);
            var files = folder.GetFiles().ToList();
            var softFiles = from items in files orderby items.CreationTime select items;
            var r = new List<string>();
            if (query.PageIndex == 0) query.PageIndex = 1;
            if (query.PageIndex <= 0 || query.PageCount <= 0) return null;
            var skip = (query.PageIndex - 1) * query.PageCount;
            var cc = 0;
            foreach (var file in softFiles)
            {
                if (file.Extension.ToUpper() != ".ZIP")
                {
                    File.Delete(file.FullName);
                    continue;
                }
                cc++;
                if (cc > skip && cc - skip <= query.PageCount)
                {
                    r.Add(file.FullName);
                }
            }
            var pr = new PagedList<string>(r)
            {
                PageIndex = query.PageIndex,
                PageSize = r.Count,
                TotalCount = cc,
                PageCount = (int)Math.Ceiling((double)cc / query.PageCount)
            };
            return pr;
        }

        /// <summary>
        /// 添加一条要发送的文件
        /// </summary>
        /// <param name="file"></param>
        public static void AddFilesToSend(string file)
        {
            _files.Enqueue(file);
        }

        /// <summary>
        /// 添加要发送的数据文件地址
        /// </summary>
        /// <param name="file"></param>
        public static int AddFile(string file)
        {
            if (!_isDataShield) return -1;
            Log.Debug().Message("添加要发送的数据：{0}", file).Write();
            if (!File.Exists(file))
            {
                Log.Error().Message("没有找到文件：{0}", file).Write();
                return -1;
            }
            //压缩文件
            var fn = Path.GetFileName(file);
            var target = Path.Combine(DataPath, fn);
            if (File.Exists(target))
            {
                Log.Debug().Message("文件：{0}已存在", file).Write();
                if (!_files.Contains(target)) _files.Enqueue(target);
                InsertRecord(fn);
                return 1;
            }
            var r = Compress(file, target);
            if (r > 0)
            {
                _files.Enqueue(target);
                InsertRecord(fn);
            }
            return r;
        }

        /// <summary>
        /// 添加要发送的数据文件地址
        /// </summary>
        /// <param name="files"></param>
        public static int AddFiles(List<string> files)
        {
            if (!_isDataShield) return -1;
            var c = 0;
            foreach (var file in files)
            {
                Log.Debug().Message("添加要发送的数据：{0}", file).Write();
                if (!File.Exists(file))
                {
                    Log.Error().Message("没有找到文件：{0}", file).Write();
                    return -1;
                }
                //压缩文件
                var fn = Path.GetFileName(file);
                var target = Path.Combine(DataPath, fn);
                if (File.Exists(target))
                {
                    Log.Debug().Message("文件：{0}已存在", file).Write();
                    if (!_files.Contains(target)) _files.Enqueue(target);
                    InsertRecord(fn);
                    continue;
                }
                var r = Compress(file, target);
                if (r > 0)
                {
                    _files.Enqueue(target);
                    InsertRecord(fn);
                    c++;
                }
            }

            return c;
        }

        public static bool TestConnect(string ip, int port)
        {
            if (!_isDataShield) return false;
            if (_isSending) return true;
            var c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            var remoteEp = new IPEndPoint(IPAddress.Parse(ip.Replace(" ", "")), port);
            try
            {
                c.Connect(remoteEp);

                c.Close();
                _connect = true;

                Log.Debug("连接成功");
                return true;
            }
            catch
            {
                _connect = false;
                Log.Debug("连接失败");
                return false;
            }
            finally
            {
                c.Close();
            }
        }

        public static PagedList<SendRecord> GetRecords(QueryBase query)
        {
            var sql = "SELECT * FROM [SendRecord]  ORDER BY [CreateTime] DESC LIMIT " + (query.PageIndex - 1) * query.PageCount +
                      "," + query.PageCount;
            var con = new SQLiteConnection(SqliteHelper.CreateConnectionString(SendRecord));
            con.Open();
            var sr = new List<SendRecord>();
            using (var cmd = con.CreateCommand())
            {
                try
                {
                    var reader = SqliteHelper.ExecuteReader(cmd, sql, null);
                    while (reader.Read())
                    {
                        var ti = new SendRecord
                        {
                            FileName = reader["FileName"] + "",
                            CreateTime = reader["CreateTime"].ToDateTime(),
                            SendCount = reader["SendCount"].ToInt32(),
                            Id = reader["Id"] + "" == "-1" ? "--" : reader["Id"] + ""
                        };
                        if (reader["End"] + "" != "") ti.End = reader["End"].ToDateTime();
                        if (reader["Start"] + "" != "") ti.Start = reader["Start"].ToDateTime();
                        sr.Add(ti);
                    }

                    reader.Close();
                }
                catch (Exception e)
                {
                    Log.Error().Exception(e).Message("查询数据盾发送记录出错:{0}",  sql).Write();
                    if (con.State != ConnectionState.Closed) con.Close();
                    return null;
                }
            }

            var count = 0;
            if (con.State != ConnectionState.Open) con.Open();
            try
            {
                count = SqliteHelper.ExecuteScalar(SendRecord, "SELECT COUNT(1) FROM [SendRecord]", null).ToInt32();
            }
            catch (Exception e)
            {
                Log.Error().Exception(e).Message("获取试验记录总数出错:{0}",  "SELECT COUNT(1) FROM [SendRecord]").Write();
                if (con.State != ConnectionState.Closed) con.Close();
                return null;
            }

            if (con.State != ConnectionState.Closed) con.Close();

            return new PagedList<SendRecord>(sr)
            {
                TotalCount = count,
                PageIndex = query.PageIndex,
                PageSize = sr.Count,
                PageCount = (int)Math.Ceiling((double)count / query.PageCount)
            };
        }

        public static int DeleteRecords()
        {
            var sql = "DELETE FROM [SendRecord] WHERE [END] IS NOT NULL";
            try
            {
                return SqliteHelper.ExecuteNonQuery(SendRecord, sql, null);
            }
            catch (Exception e)
            {
                Log.Error().Exception(e).Message("删除发送成功记录失败").Write();
                return -1;
            }
        }

        public static int DeleteRecord(string file)
        {
            var sql = "DELETE FROM [SendRecord] WHERE [FileName]=@FileName";
            try
            {
                var fn = Path.Combine(DataPath, file);
                if (File.Exists(fn))
                {
                    File.Delete(fn);
                }
                return SqliteHelper.ExecuteNonQuery(SendRecord, sql, file);
            }
            catch (Exception e)
            {
                Log.Error().Exception(e).Message("删除 {0} 记录失败",  file).Write();
                return -1;
            }
        }

        private static void _send()
        {
            while (_isDataShield)
            {
                //if (DataManage.IsTest)
                //{
                //    Log.Debug("试验中不发送数据,等待5分钟后检测");
                //    Thread.Sleep(300000);
                //    continue;
                //}
                if (!_connect)
                {
                    var wait = 5;
                    Log.Debug().Message("检测到连接失败,等待等待{0}分钟后重试", wait).Write();
                    Thread.Sleep((int)TimeSpan.FromMinutes(wait).TotalMilliseconds);
                }

                if (_files.Count > 0)
                {
                    _isSending = true;
                    var file = _files.Dequeue();
                    Log.Debug().Message("开始发送文件 {0}", file).Write();

                    if (File.Exists(file))
                    {
                        try
                        {
                            var fi = new FileInfo
                            {
                                PackSize = 1024,
                                Action = ActionEnum.All,
                                CanChange = true,
                                FileIndate = -1,
                                FileName = Path.GetFileName(file),
                                FullName = file,
                                IsRecord = true,
                                IsSendLocal = false,
                                IsSendRemote = false,
                                Md5 = Md5Helper.CreateMd5(file)
                            };
                            var st = DateTime.Now;
                            try
                            {
                                _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                                _client.Connect(_remoteEp);
                                _connect = true;
                            }
                            catch
                            {
                                _connect = false;
                                continue;
                            }
                            try
                            {
                                st = DateTime.Now;
                                var r = _sender.Send(_client, fi);
                                var et = DateTime.Now;
                                if (r == 0)
                                {
                                    Log.Debug().Message("文件 {0} 发送完成", file).Write();
                                    File.Delete(file);
                                    UpdateSuccess(Path.GetFileName(file), st, et);
                                }
                                else
                                {
                                    UpdateFail(Path.GetFileName(file), st);
                                }
                                _client.Close();

                                _isSending = false;
                                Thread.Sleep(100);
                            }
                            catch (ThreadAbortException)
                            {
                            }
                            catch (Exception ex)
                            {
                                _connect = false;
                                _isSending = false;
                                Log.Error().Exception(ex).Message("发送文件{0}失败",  file).Write();
                            }
                        }
                        catch (ThreadAbortException)
                        {
                        }
                        catch (Exception ex)
                        {
                            Log.Error().Exception(ex).Message("发送文件{0}失败",  file).Write();
                        }
                    }
                }
                else
                {
                    try
                    {
                        GetFiles();
                        if (_files.Count == 0)
                        {
                            var wait = 60;

                            Log.Debug().Message("文件列表为空,等待{0}秒", wait).Write();
                            Thread.Sleep(wait * 1000);
                        }
                    }
                    catch (ThreadAbortException)
                    {
                    }
                }
            }
        }

        private static int Compress(string sourceFile, string target)
        {
            var targetFile = sourceFile + ".zip";
            if (File.Exists(target)) return 1;
            var r = ZipTool.Zip(sourceFile, targetFile, string.Empty);
            File.Move(targetFile, target);
            return r;
        }

        private static void GetFiles()
        {
            if (!Directory.Exists(DataPath)) return;
            if (!_isDataShield) return;
            Log.Debug().Message("获取{0}下要发送的所有文件", DataPath).Write();
            var folder = new DirectoryInfo(DataPath);
            var files = folder.GetFiles().ToList();
            var softFiles = from items in files orderby items.CreationTime select items;
            Log.Debug(string.Format("获取文件数量：{0}", softFiles.Count()));
            foreach (var file in softFiles)
            {
                if (file.Name.Contains("sendrecord")) continue;
                if (!string.IsNullOrEmpty(file.Extension))
                {
                    File.Delete(file.FullName);
                    continue;
                }
                if (!_files.Contains(file.FullName))
                {
                    _files.Enqueue(file.FullName);
                }
            }
        }

        private static void InsertRecord(string fn)
        {
            if (!File.Exists(SendRecord)) return;
            var ns = fn.Split('_');
            var id = "-1";
            if (ns.Length > 1) id = ns[1];

            var c = SqliteHelper.ExecuteScalar(SendRecord,
                "SELECT COUNT(1) FROM [SendRecord] WHERE [FileName]=@FileName", fn);
            if (c != null && c.ToInt32() > 0)
            {
                SqliteHelper.ExecuteNonQuery(SendRecord, "DELETE FROM [SendRecord] WHERE [FileName]=@FileName", fn);
            }

            var sql = "INSERT INTO [SendRecord] ([FileName] ,[id],[CreateTime], [SendCount]) VALUES (@FileName,@Id,@CreateTime,@SendCount)";
            SqliteHelper.ExecuteNonQuery(SendRecord, sql, fn, id, DateTime.Now, 0);
        }

        private static int UpdateSuccess(string fn, DateTime start, DateTime end)
        {
            string sql = "";
            var r = 0;
            try
            {
                if (!_isAutoDelete)
                {
                    sql = "Update [SendRecord] SET [Start]=@Start,[End]=@End WHERE [FileName]=@FileName";
                    r = SqliteHelper.ExecuteNonQuery(SendRecord, sql, start, end, fn);
                }
                else
                {
                    sql = "DELETE FROM  [SendRecord] WHERE [FileName]=@FileName";
                    r = SqliteHelper.ExecuteNonQuery(SendRecord, sql, fn);
                }
            }
            catch (Exception e)
            {
                Log.Error().Exception(e).Message("更新文件 {0} 发送成功 失败",  fn).Write();
            }

            return r;
        }

        private static int UpdateFail(string fn, DateTime start)
        {
            string sql = "";
            var r = 0;
            try
            {
                sql = "Update [SendRecord] SET [Start]=@Start,[SendCount]=[SendCount]+1 WHERE [FileName]=@FileName";
                r = SqliteHelper.ExecuteNonQuery(SendRecord, sql, start, fn);

            }
            catch (Exception e)
            {
                Log.Error().Exception(e).Message("更新文件 {0} 发送失败 失败",  fn).Write();
            }

            return r;
        }

        private static void CreateTable()
        {
            var sql = "CREATE TABLE [SendRecord] ([FileName] VARCHAr(100) NOT NULL PRIMARY,[Id] VARCHAR(10), [CreateTime] DATETIME, [Start] DATETIME,[End] DATETIME,[SendCount] INTEGER)";
            SqliteHelper.ExecuteNonQuery(SendRecord, sql, null);
        }

        private static bool HasTable()
        {
            var sql = "SELECT COUNT(1) FROM sqlite_master where type='table' and name='SendRecord'";
            var o = SqliteHelper.ExecuteScalar(SendRecord, sql, null);
            if (o == null) return false;
            return o.ToInt32() > 0;
        }
    }
}
